package xqueueredis

import (
	"context"
	"errors"
	"time"

	"gitee.com/xiaoyutab/xgotool/individual/xqueue"
	"github.com/go-redis/redis/v8"
)

// 队列名称信息
type QueuesList struct {
	Addr  string                 // Redis地址
	Qname string                 // 队列名称
	execs func(dat []byte) error // 监听到变量后执行的函数
	pass  string                 // Redis密码
	db    int                    // Redis数据库 0~15
	push  int                    // 推送方向 0为左侧，1为右侧
	pop   int                    // 弹出方向 0为右侧，1为左侧
	stop  chan bool              // 停止监听的通道
}

// 队列任务设置
func (c *QueuesList) Set(byt []byte) error {
	// 打开redis连接
	rdb := redis.NewClient(&redis.Options{
		Addr:     c.Addr,
		Password: c.pass,
		DB:       c.db,
	})
	if c.push == 0 {
		rdb.LPush(context.Background(), c.Qname, byt)
	} else {
		rdb.RPush(context.Background(), c.Qname, byt)
	}
	return nil
}

// 设置延时队列监听
func (c *QueuesList) SetDef(dat []byte, t time.Duration) error {
	if c == nil {
		return errors.New("Queues对象不能为nil")
	}
	// 因redis的list结构不支持延时推送，所以这里的延时使用的是time.Sleep的方式进行实现
	go func() {
		time.Sleep(t)
		// 打开redis连接
		rdb := redis.NewClient(&redis.Options{
			Addr:     c.Addr,
			Password: c.pass,
			DB:       c.db,
		})
		if c.push == 0 {
			rdb.LPush(context.Background(), c.Qname, dat)
		} else {
			rdb.RPush(context.Background(), c.Qname, dat)
		}
	}()
	return nil
}

// 队列监听
func (c *QueuesList) Listen(f func(dat []byte) error) error {
	if c == nil {
		return errors.New("Queues对象不能为nil")
	}
	c.execs = f
	// 打开redis连接
	rdb := redis.NewClient(&redis.Options{
		Addr:     c.Addr,
		Password: c.pass,
		DB:       c.db,
	})
	for {
		var res []string
		var err error
		if c.pop == 0 {
			res, err = rdb.BRPop(context.Background(), time.Second*10, c.Qname).Result()
		} else {
			res, err = rdb.BLPop(context.Background(), time.Second*10, c.Qname).Result()
		}
		if err != nil {
			continue
		}
		if len(res) == 2 {
			err := c.execs([]byte(res[1]))
			if err != nil {
				tm := 1
				if v, ok := _cache_redis_client.Load(res[1]); ok {
					tm = v.(int) + 1
					if tm > ErrorNum {
						_cache_redis_client.Delete(res[1])
						continue
					}
					_cache_redis_client.Store(res[1], tm)
				} else {
					_cache_redis_client.Store(res[1], tm)
				}
				go func() {
					time.Sleep(_sleep_time[tm])
					// 打开redis连接
					rdb := redis.NewClient(&redis.Options{
						Addr:     c.Addr,
						Password: c.pass,
						DB:       c.db,
					})
					rdb.LPush(context.Background(), c.Qname, []byte(res[1]))
				}()
			}
		}
		// 进行循环监听队列
		select {
		case <-c.stop:
			return nil
		default:
			time.Sleep(time.Millisecond * 10) // 休眠10毫秒
		}
	}
}

// 打开队列
//
//	addr	Redis地址
//	db		Redis数据库
//	pass	Redis密码
//	qname	队列名称
//	push	推送/弹出方向，默认为0(只传1个参数则只修改push方向，超过两个参数则忽略)
//			push[0]为推送方向，0为左侧，1为右侧
//			push[1]为弹出方向，0为右侧，1为左侧
func OpenList(addr string, db int, pass string, qname string, push ...int) xqueue.Queue {
	if addr == "" {
		addr = "localhost:6379"
	}
	rds := &QueuesList{
		Addr:  addr,
		Qname: qname,
		pass:  pass,
		db:    db,
		stop:  make(chan bool),
	}
	if len(push) >= 2 {
		rds.push = push[0]
		rds.pop = push[1]
	} else if len(push) == 1 {
		rds.push = push[0]
	}
	return rds
}

// 关闭队列
func (c *QueuesList) Stop() {
	c.stop <- true
}
